
    // 进度监控 用于loding
    function showExportTip(type) {
        if (type == 'start') {
        console.log('----导出开始',new Date());
        } else if (type && type != 'start') {
            console.log('----导出进度---',type);
        } else {
        console.log('----导出结束---',type);
        }
    }
    var newColumnList = {}  // 用来存储每一列数据类型
    var newColumnWidthList = {}  // 用来存储每一列 表格宽度
    // 初始化 数据 构建数据
    function exportExcelFromFront(params, newFn) {
        newColumnList = {}
        newColumnWidthList = {}
        var _params = params,
        cellList = Object.keys(_params.header[0]) || [],
        cellListName = Object.values(_params.header[0]) || [],
        columnList = _params.column || [],
        exportData = _params.data,
        caption = _params.caption || '',
        exportName = _params.fileName || '',
        isShowZero = _params.isShowZero
        headers = _params.headers || []
        var captionEle = '' // 表格标题
        var _headerList = []
        var headerEle = ''
        var widthNum = 1.2 // 列宽倍数 兼容宽度过小问题
        // 自行根据自己的表头 列数据做封装
        if (headers.length > 0) {//  多级表头
            var headerOne = []
            var headerTow = []
            headers.map(function (item) {
                if (item.columns) { // 多级表头 业务最多两级，需要多的自己动手 根据 __colSpan 来合并多少行 colspan表示合并多少列
                    item.__colSpan = item.columns.length || item.__colSpan
                    headerOne.push(("<th class='h' colspan=" + item.__colSpan + '>').concat(item['display'], '</th>'))
                    item.columns.map(function (itemCell) {
                        newColumnList[itemCell.columnname] = itemCell.type
                        var columnWidth = (itemCell.width || itemCell._width || 30) * widthNum
                        headerTow.push(("<th class='h' style='width:" + columnWidth + "px'>").concat(itemCell['display'], '</th>'))
                    })
                } else { // 第一级表头
                    newColumnList[item.columnname] = item.type
                    var columnWidth = (item.width || item._width || 30) * widthNum
                    headerOne.push(("<th class='h' rowspan='2' style='width:" + columnWidth + "px'>").concat(item['display'], '</th>'))
                }
            })
            // 表头组装
            headerEle = '<tr>'.concat(headerOne.join(''), '</tr>').concat('<tr>'.concat(headerTow.join(''), '</tr>'))
            // 表头标题组装
            captionEle = caption ? "<th align='left' class='headerTop'" + 'colspan=' + headerOne.length + headerTow.length + '>' + caption + '</th>' : ''
        } else { // 单表头
            columnList.map(function (item) {
                newColumnList[item.columnname] = item.type
                newColumnWidthList[item.display] = (item.width || item._width || 30) * widthNum
            })
            cellListName.map(function (itemCell) {
                _headerList.push("<th class='h' style='width:" + newColumnWidthList[itemCell] + "px'>" + itemCell + '</th>')
            })
            // 表头组装
            headerEle = '<tr>'.concat(_headerList.join(''), '</tr>')
            // 表头标题组装
            captionEle = caption ? "<tr><th align='left' class='headerTop'" + 'colspan=' + _headerList.length + '>' + caption + '</th></tr>' : ''
        }
        var exportDatas = _params.statisticsColumns || {}
        exportDatas[cellList[0]] = '合计: 共' + exportData.length + '行'
        // 运单号以及货号 不为第一项去除 运单号以及货号的合计问题
        if (cellList[0] != 'unit') {
        if (exportDatas && exportDatas['unit']) {
            exportDatas['unit'] = ''
        }
        }
        if (cellList[0] != 'billno') {
        if (exportDatas && exportDatas['billno']) {
            exportDatas['billno'] = ''
        }
        }
        exportData = exportData.concat(exportDatas)
        setTimeout(function () {
            setXlsxData({
                arr: exportData,
                headerList: cellList,
                isShowZero: isShowZero,
                captionEle: captionEle,
                headerEle: headerEle,
                exportName: exportName,
                _cellList: [],
                newFn: newFn,
                customColumnsSHowZero: params.customColumnsSHowZero
            })
        })
    }
    // 最终导出表格
    function newExportWebXlsx(params) {
        if (params.exportName && params.exportName.indexOf('.xls') === -1) {
            params.exportName = params.exportName + new Date().Format('yyyyMMddhhmmss') + '.xls'
        }
        var cellEle = params._cellList.join('')
        var excelContent = ''.concat(params.captionEle).concat(params.headerEle).concat(cellEle)
        var excelFile =
        "<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:x='urn:schemas-microsoft-com:office:excel' xmlns='http://www.w3.org/TR/REC-html40'>"
        excelFile +=
        '<head><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>sheet</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]-->'
        excelFile +=
        '<style>tr td{text-align:left;height:25px;} .T{vnd.ms-excel.numberformat:yyyy-MM-dd\\ hh\\:mm\\:ss;} .N0{vnd.ms-excel.numberformat:0;text-align:right;} .N1{vnd.ms-excel.numberformat:0.0;text-align:right;} .N2{vnd.ms-excel.numberformat:0.00;text-align:right;} .N3{vnd.ms-excel.numberformat:0.000;text-align:right;} .N4{vnd.ms-excel.numberformat:0.0000;text-align:right;} .S{vnd.ms-excel.numberformat: @;} .headerTop{height:35px;} .h{font-size: 14.7;}</style></head>'
        excelFile += "<body><table border='1' style='font-size: 13.4;font-family: 微软雅黑;'>"
        excelFile += excelContent
        excelFile += '</table></body>'
        excelFile += '</html>'
        var blob = new Blob(['\uFEFF' + excelFile], { type: 'application/vnd.ms-excel;charset=utf-8' })
        if (window.navigator && window.navigator.msSaveOrOpenBlob) {
            navigator.msSaveOrOpenBlob(blob, params.exportName)
        } else {
            // 兼容不同浏览器的URL对象
            var url = window.URL || window.webkitURL || window.moxURL
            // 创建下载链接
            var downloadHref = url.createObjectURL(blob)
            // 创建a标签并为其添加属性
            var linka = document.createElement('a')
            linka.href = downloadHref
            linka.style.display = 'none'
            linka.setAttribute('download', params.exportName)
            // 触发点击事件执行下载
            // 火狐浏览器上a标签点击导出无效。解决办法：需要先将a标签添加到当前页面上，在执行click，之后再移除该节点，而不能直接执行click
            document.body.appendChild(linka)
            linka.click()
            // 下载完成进行释放
            url.revokeObjectURL(linka.href)
            document.body.removeChild(linka)
        }
        console.log('----导出结束', new Date())
        params.newFn()
    }
    // 分批构建表格
    function setXlsxData(parms) {
        var s = 1000
        var sum = Math.ceil(parms.arr.length / s)
        var newObj = {}
        for (var i = 0; i < sum; i++) {
            ;(function (j) {
                setTimeout(function () {
                var sIn = j * s
                var endI = sIn + s
                if (j == sum - 1 && parms.arr.length < s * sum) {
                    endI = parms.arr.length
                }
                var newArr = parms.arr.slice(sIn, endI)
                newObj[j] = toForXlsxData(newArr, parms.headerList, parms.isShowZero, parms.customColumnsSHowZero)
                checkDataOver(sum, newObj, parms, j)
                }, j * 200)
            })(i)
        }
    }
    // 检查数据是否组装完成
    function checkDataOver(sum, newObj, parms, j) {
        var status = Math.round((j / sum) * 100)
        var statusText = '数据处理进度' + status + '%'
        if (sum === Object.keys(newObj).length) {
            for (var i = 0; i < sum; i++) {
                parms._cellList = parms._cellList.concat(newObj[i])
            }
            parms.newFn('导出文件生成中...')
            setTimeout(function () {
                newExportWebXlsx(parms)
            })
        } else {
            parms.newFn(statusText)
        }
    }
    // 构建每一个单元格样式
    function toForXlsxData(arr, headerList, isShowZero, customColumnsSHowZero) {
        var newArr = []
        // var getNeedWVDataNumber = getNeedWVData()
        for (var i = 0; i < arr.length; i++) {
            var _itemRow = []
            for (var k = 0; k < headerList.length; k++) {
                if (arr[i][headerList[k]]) {
                    if (newColumnList[headerList[k]] === 'bigint' || newColumnList[headerList[k]] === 'string') {
                        _itemRow[k] = "<td class='S'>" + arr[i][headerList[k]] + '</td>'
                    } else if (newColumnList[headerList[k]] === 'float') {
                        var classNum = toFloatNumCss(arr[i][headerList[k]])
                        _itemRow[k] = "<td class='" + classNum + "'>" + arr[i][headerList[k]] + '</td>'
                    } else if (newColumnList[headerList[k]] === 'date') {
                        _itemRow[k] = "<td class='T'>" + arr[i][headerList[k]] + '</td>'
                    } else {
                        _itemRow[k] = '<td>' + arr[i][headerList[k]] + '</td>'
                    }
                } else {
                    // 是否显示0
                    _itemRow[k] = '<td>' + (isShowZero ? 0 : '') + '</td>'
                }
                // 处理以0开头的数据
                if (arr[i][headerList[k]] && String(arr[i][headerList[k]]).startsWith('0') && String(arr[i][headerList[k]]).indexOf('.') == -1) {
                    _itemRow[k] = "<td class='S'>" + arr[i][headerList[k]] + '</td>'
                }
            }
            newArr.push('<tr>'.concat(_itemRow.join(''), '</tr>'))
        }
        return newArr
    }
    // 特殊数据类型做处理
    function toFloatNumCss(params) {
        var numCss = 'N0'
        var number = params
        if (String(number) && String(number).indexOf('.') == -1) {
            // 非小数的，直接返回
            return numCss
        }
        if (String(number).indexOf('.') > -1 && String(number)) {
            // 存在小数点
            number = parseFloat(params)
            var x = String(number).indexOf('.') + 1 //小数点的位置
            var y = String(number).length - x //小数的位数
            y = y > 4 ? 4 : y
            switch (y) {
                case 1:
                numCss = 'N1'
                break
                case 2:
                numCss = 'N2'
                break
                case 3:
                numCss = 'N3'
                break
                default:
                numCss = 'N4'
                break
            }
        }
        return numCss
    }